package edu.hawaii.myisern.xmldatabase;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.io.FileNotFoundException;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;
import javax.xml.bind.JAXBException;
import org.junit.Before;
import org.junit.Test;
import org.xml.sax.SAXException;
import edu.hawaii.myisern.collaborations.jaxb.Collaboration;
import edu.hawaii.myisern.collaborations.jaxb.Collaborations;
import edu.hawaii.myisern.organizations.jaxb.Organization;
import edu.hawaii.myisern.researchers.jaxb.Researcher;

/**
 * JUNIT test class to test the XmlLoader class.
 * 
 * @author Ben Karsin, Shaoxuan Zhang
 * 
 */
public class TestXmlLoader {
  static final String TEST_COLLAB_1 = "UM-UH-HPCS";
  static final String TEST_COLLAB_2 = "UM-UH-HPCS2";
  static final String TEST_COLLAB_3 = "UH-SGH";
  static final String TEST_ORG_1 = "University_of_Hawaii";
  static final String TEST_ORG_2 = "University_of_Maryland";
  static final String TEST_ORG_3 = "Seattle_Grace_Hospital";
  static final String TEST_RSC_1 = "Philip_Johnson";
  static final String TEST_RSC_2 = "Victor_Basili";
  static final String TEST_RSC_3 = "Marvin_Zelkowitz";
  static final String TEST_RSC_4 = "Meredith_Grey";
  static final String TEST_RSC_5 = "Cristina_Yang";
  static final String TEST = "test";

  private XmlLoader loader;

  /**
   * Initial a XmlLoader for every test.

   * @throws JAXBException - Exception when marshalling.
   * @throws SAXException If JAXB throws an exception.
   */
  @Before
  public void setUp() throws JAXBException, SAXException {
    loader = new XmlLoader("test");
  }

  /**
   * Tests the toUniqueId() method.
   */
  @Test
  public void testToUniqueId() {
    assertEquals("Testing toUniqueId", XmlLoader.toUniqueId("A B C"), "A_B_C");
  }

  /**
   * Tests the Checking methods in a variety of situations.
   */

  @Test
  public void testChecking() {
    assertEquals("Testing Checking", "", loader.errorMessage);
  }

  /**
   * Tests semanticCheck(Collaboration).
   */
  @Test
  public void testSemanticCheckCollaboration() {
    Collaboration testCollaboration = loader.getCollaborations().getCollaboration().get(0);
    // Adding error information
    testCollaboration.getCollaboratingOrganizations().getCollaboratingOrganization().add(TEST);
    testCollaboration.getYears().getYear().add(new BigInteger("1989"));
    testCollaboration.getYears().getYear().add(new BigInteger("2011"));
    String errorMessage = loader.semanticCheck(testCollaboration);
    assertTrue("Testing semanticCheck(Collaboration), check years violation", errorMessage
        .contains("1989")
        && errorMessage.contains("2011")
        && errorMessage.contains("Should be between 1990 and 2010."));
  }

  /**
   * Tests semanticCheck(Organization).
   */
  @Test
  public void testSemanticCheckOrganization() {
    Organization testOrganization = loader.getOrganizations().getOrganization().get(0);
    testOrganization.setContact("testContact");
    testOrganization.getAffiliatedResearchers().getAffiliatedResearcher().add("testAR");
    String errorMessage = loader.semanticCheck(testOrganization);
    assertTrue("Testing semanticCheck(Organization), check contact violation", errorMessage
        .contains("Valid contact is missing"));
  }

  /**
   * Tests semanticCheck(Researcher).
   */
  @Test
  public void testSemanticCheckResearcher() {
    Researcher testResearcher = loader.getResearchers().getResearcher().get(0);
    testResearcher.setOrganization("testOrg");
    String errorMessage = loader.semanticCheck(testResearcher);
    assertTrue("Testing semanticCheck(Researcher), check organization violation", 
        errorMessage.contains(testResearcher.getName()));
    assertTrue("Testing semanticCheck(Researcher), check organization violation", 
        errorMessage.contains("valid Organization"));
  }

  /**
   * Tests getCollaboration() to check that it returns the correct string.
   */
  @Test
  public void testGetCollaboration() {
    for (Collaboration collaboration : loader.getCollaborations().getCollaboration()) {
      assertSame("Test getCollaboration()", collaboration, loader.getCollaboration(XmlLoader
          .toUniqueId(collaboration.getName())));
    }
  }

  /**
   * Tests getOrganization() to check that it returns the correct string.
   */
  @Test
  public void testGetOrganization() {
    for (Organization organization : loader.getOrganizations().getOrganization()) {
      assertSame("Test getOrganization()", organization, loader.getOrganization(XmlLoader
          .toUniqueId(organization.getName())));
    }
  }

  /**
   * Tests getResearcher() to check that it returns the correct string.
   */
  @Test
  public void testGetResearcher() {
    for (Researcher researcher : loader.getResearchers().getResearcher()) {
      assertSame("Test getResearcher()", researcher, loader.getResearcher(XmlLoader
          .toUniqueId(researcher.getName())));
    }
  }

  /**
   * Tests the getCollaborationsByOrganization() method to make sure it returns the correct
   * collaboration.
   */
  @Test
  public void testGetCollaborationsByOrganization() {
    Collaborations testCollaborations = loader.getCollaborationsByOrganization(TEST_ORG_1);
    assertEquals("Check that UH has 3 collaborations", 3, testCollaborations.getCollaboration()
        .size());
    testCollaborations = loader.getCollaborationsByOrganization(TEST_ORG_3);
    assertEquals("Check that SGU has 1 collaboration", 1, testCollaborations.getCollaboration()
        .size());
    assertEquals("Check that SGU's collaboration's name is UH-SGH", TEST_COLLAB_3,
        testCollaborations.getCollaboration().get(0).getName());
    testCollaborations = loader.getCollaborationsByOrganization(TEST);
    assertNull("Check that no collaboration has organization name test", testCollaborations);
  }

  /**
   * Tests the getCollaborationsByResearcher() method to make sure it returns the correct
   * collaboration.
   */
  @Test
  public void testGetCollaborationsByResearcher() {
    Collaborations testCollaborations = loader.getCollaborationsByResearcher(TEST_RSC_1);
    assertEquals("Check that Philip Johnson attend 3 collaborations", 3, testCollaborations
        .getCollaboration().size());
    testCollaborations = loader.getCollaborationsByResearcher(TEST_RSC_4);
    assertEquals("Check that Meredith Grey attend 1 collaboration", 1, testCollaborations
        .getCollaboration().size());
    assertEquals("Check that Meredith Grey's collaboration is UH-SGH", TEST_COLLAB_3,
        testCollaborations.getCollaboration().get(0).getName());
    testCollaborations = loader.getCollaborationsByResearcher(TEST);
    assertNull("Check that no collaboration has researcher name test", testCollaborations);
  }

  /**
   * Tests the getCollaborationsByYear() method to make sure it returns the correct collaboration.
   */
  @Test
  public void testGetCollaborationsByYear() {
    Collaborations testCollaborations = loader.getCollaborationsByYear(2006);
    assertEquals("Check that 2 collaborations in year 2006", 2, testCollaborations
        .getCollaboration().size());
    testCollaborations = loader.getCollaborationsByYear(2000);
    assertNull("Check that no collaboration in year 2000", testCollaborations);

  }

  /**
   * Tests getNumberOfCollaborations() to make sure it returns the correct number of collaborations.
   */
  @Test
  public void testGetNumberOfCollaborations() {
    Map<String, Integer> organizationMap = loader.getNumberOfCollaborations();
    assertEquals("Check that UH has 3 collaborations", 3, organizationMap.get(TEST_ORG_1)
        .intValue());
    assertEquals("Check that UM has 3 collaborations", 2, organizationMap.get(TEST_ORG_2)
        .intValue());
    assertEquals("Check that SGH has 3 collaborations", 1, organizationMap.get(TEST_ORG_3)
        .intValue());
  }

  /**
   * Tests getOrganizationCollaborateEquals() to make sure it returns the correct organizations (and
   * null if there are none).
   */
  @Test
  public void testGetOrganizationCollaborateEquals() {
    assertEquals("Check that 1 organization engage in 3 collaboration", 1, loader
        .getOrganizationCollaborateEquals(3).size());
    assertEquals("Check that only UH engage in 2 collaboration", TEST_ORG_2, loader
        .getOrganizationCollaborateEquals(2).get(0));
    assertEquals("Check that no organization engage in 4 collaboration", 0, loader
        .getOrganizationCollaborateEquals(4).size());

  }

  /**
   * Tests getOrganizationCollaborateEquals() to make sure it returns the correct organizations (and
   * null if there are none).
   */
  @Test
  public void testGetOrganizationCollaborateGreaterThan() {
    assertEquals("Check that 2 organization engage in more than 1 collaboration", 2, loader
        .getOrganizationCollaborateGreaterThan(1).size());
    assertEquals("Check that no organization engage in more than 3 collaboration", 0, loader
        .getOrganizationCollaborateGreaterThan(3).size());

  }

  /**
   * Tests addCollaboration().
   */
  @Test
  public void testAddCollaboration() {
    Collaboration testCollaboration = loader.getCollaboration(TEST_COLLAB_1);
    assertSame("Check that add return right object when adding existed collaboration",
        testCollaboration, loader.add(testCollaboration));
    testCollaboration = new Collaboration();
    testCollaboration.setName(null);
    assertNull("Check that add return null when adding new collaboration with no name", loader
        .add(testCollaboration));
    testCollaboration.setName(TEST);
    assertNull("Check that add return null when adding new collaboration", loader
        .add(testCollaboration));
  }

  /**
   * Tests addOrganization().
   */
  @Test
  public void testAddOrganization() {
    Organization testOrganization = loader.getOrganization(TEST_ORG_1);
    assertSame("Check that add return right object when adding existed organization",
        testOrganization, loader.add(testOrganization));
    testOrganization = new Organization();
    testOrganization.setName(null);
    assertNull("Check that add return null when adding new organization with no name", loader
        .add(testOrganization));
    testOrganization.setName(TEST);
    assertNull("Check that add return null when adding new organization", loader
        .add(testOrganization));
  }

  /**
   * Tests addResearcher().
   */
  @Test
  public void testAddResearcher() {
    Researcher testResearcher = loader.getResearcher(TEST_RSC_1);
    assertSame("Check that add return right object when adding existed researcher", testResearcher,
        loader.add(testResearcher));
    testResearcher = new Researcher();
    testResearcher.setName(null);
    assertNull("Check that add return null when adding new researcher with no name", loader
        .add(testResearcher));
    testResearcher.setName(TEST);
    assertNull("Check that add return null when adding new researcher", loader.add(testResearcher));
  }

  /**
   * Test hasCollaboration().
   */
  @Test
  public void testHasCollaboration() {
    assertTrue("Check that hasCollaboration() return true when asking for UM-UH-HPCS", loader
        .hasCollaboration(TEST_COLLAB_1));
    assertTrue("Check that hasCollaboration() return true when asking for UM-UH-HPCS2", loader
        .hasCollaboration(TEST_COLLAB_2));
    assertTrue("Check that hasCollaboration() return true when asking for UH-SGH", loader
        .hasCollaboration(TEST_COLLAB_3));
    assertFalse("Check that hasCollaboration() return false when asking for test", loader
        .hasCollaboration(TEST));
    assertFalse("Check that hasCollaboration() return false when asking for UH", loader
        .hasCollaboration(TEST_ORG_1));
    assertFalse("Check that hasCollaboration() return false when asking for null", loader
        .hasCollaboration(null));
  }

  /**
   * Test hasOrganization().
   */
  @Test
  public void testHasOrganization() {
    assertTrue("Check that hasOrganization return true when asking for UH", loader
        .hasOrganization(TEST_ORG_1));
    assertTrue("Check that hasOrganization return true when asking for UM", loader
        .hasOrganization(TEST_ORG_2));
    assertFalse("Check that hasOrganization return false when asking for test", loader
        .hasOrganization(TEST));
    assertFalse("Check that hasOrganization return false when asking for UM-UH-HPCS", loader
        .hasOrganization(TEST_COLLAB_1));
    assertFalse("Check that hasOrganization return false when asking for null", loader
        .hasOrganization(null));

  }

  /**
   * Test hasResearcher().
   */
  @Test
  public void testHasResearcher() {
    assertTrue("Check that hasResearcher return true when asking for Philip Johnson", loader
        .hasResearcher(TEST_RSC_1));
    assertTrue("Check that hasResearcher return true when asking for Marvin Zelkowitz", loader
        .hasResearcher(TEST_RSC_3));
    assertFalse("Check that hasResearcher return false when asking for test", loader
        .hasResearcher(TEST));
    assertFalse("Check that hasResearcher return false when asking for UM", loader
        .hasResearcher(TEST_ORG_2));
    assertFalse("Check that hasResearcher return false when asking for null", loader
        .hasResearcher(null));
  }

  /**
   * Test isSaved().
   */
  @Test
  public void testIsSaved() {
    assertTrue("Check that isSaved is true when no modification is performed", loader.isSaved());
    loader.add(loader.getCollaboration(TEST_COLLAB_1));
    assertFalse("Check that isSaved is false when a modification is performed", loader.isSaved());
  }

  /**
   * Test remove().
   */
  @Test
  public void testRemove() {
    assertTrue("Check that remove return true when removing UH", loader.remove(TEST_COLLAB_1));
    assertFalse("Check if " + TEST_COLLAB_1 + " is gone", loader.hasCollaboration(TEST_COLLAB_1));
    assertTrue("Check that remove return true when removing UH", loader.remove(TEST_ORG_1));
    assertFalse("Check if " + TEST_ORG_1 + " is gone", loader.hasOrganization(TEST_ORG_1));
    assertTrue("Check that remove return true when removing UH", loader.remove(TEST_RSC_1));
    assertFalse("Check if " + TEST_RSC_1 + " is gone", loader.hasResearcher(TEST_RSC_1));
    assertFalse("Check that remove return false when removing test", loader.remove(TEST));
  }

  /**
   * Test save().
   * 
   * @throws JAXBException - Exception when marshalling.
   * @throws FileNotFoundException - Problems when opening file.
   * @throws SAXException If JAXB throws an exception.
   */
  @Test
  public void testSave() throws FileNotFoundException, JAXBException, SAXException {
    assertEquals("Check that save return no errors", 0, loader.save().length());
  }

  /**
   * Test getCollaborationNames().
   */
  @Test
  public void testGetCollaborationNames() {
    List<String> nameList = loader.getCollaborationNames();
    assertTrue("Check that UM-UH-HPCS is in collaboration nameList", nameList
        .contains(TEST_COLLAB_1));
    assertFalse("Check that UH is not in collaboration nameList", nameList
        .contains("University of Hawaii"));
  }

  /**
   * Test getOrganizationNames().
   */
  @Test
  public void testGetOrganizationNames() {
    List<String> nameList = loader.getOrganizationNames();
    assertTrue("Check that UH is in organization nameList", nameList
        .contains("University of Hawaii"));
    assertFalse("Check that UM-UH-HPCS is not in organization nameList", nameList
        .contains(TEST_COLLAB_1));
  }

  /**
   * Test getResearcherNames().
   */
  @Test
  public void testGetResearcherNames() {
    List<String> nameList = loader.getResearcherNames();
    assertTrue("Check that Philip is in researcher nameList", nameList.contains("Philip Johnson"));
    assertFalse("Check that UH is not in researcher nameList", nameList
        .contains("University of Hawaii"));
  }
  
  /**
   * Test joinListToString.
   */
  @Test public void testJoinListToString() {
    Collaboration collaboration = this.loader.getCollaboration(TEST_COLLAB_1);
    String string = XmlLoader.joinListToString(
        collaboration.getCollaboratingOrganizations().getCollaboratingOrganization(), "\n");
    assertTrue("UM should in the string",string.contains("University of Maryland"));
  }
  
  /**
   * Test joinBigIntegerListToString.
   */
  @Test public void testJoinBigIntegerListToString() {
    Collaboration collaboration = this.loader.getCollaboration(TEST_COLLAB_1);
    String string = XmlLoader.joinBigIntegerListToString(collaboration.getYears().getYear(), "\n");
    assertTrue("2006 should in the string",string.contains("2006"));
    assertTrue("2005 should in the string",string.contains("2005"));
    assertFalse("2003 should not in the string",string.contains("2003"));
  }
  
  /**
   * Test splitStringToBigIntegerList.
   */
  @Test public void testSplitStringToBigIntegerList () {
    String string = "2005,2006,2008";
    List<BigInteger> years = XmlLoader.splitStringToBigIntegerList(string, ",");
    assertEquals("There should be 3 element", 3, years.size());
  }
  
  /**
   * Test splitStringToStringList.
   */
  @Test public void testSplitStringToList () {
    String string = "2005,2006,2008";
    List<String> years = XmlLoader.splitStringToList(string, ",");
    assertEquals("There should be 3 element", 3, years.size());
    assertTrue("2005 should in the list",years.contains("2005"));
    assertTrue("2006 should in the list",years.contains("2006"));
    assertTrue("2008 should in the list",years.contains("2008"));
  }
}
